/*
* docker.cpp
 *
 *  Created on: Jan 3, 2025
 *      Author: bafc
 */

#include <array>
#include <vector>
#include <string.h>
#include <iostream>
#include <licensecc/datatypes.h>
#include <licensecc_properties.h>
#include "../base/base.h"
#include "../os/network.hpp"
#include "hw_identifier.hpp"
#include "docker_strategy.hpp"

#include <fstream>

namespace license {
namespace hw_identifier {

#include <unistd.h>

class DockerDetector {
public:
    // 检查是否在容器中运行
    static bool isRunningInContainer() {
        std::ifstream cgroupFile("/proc/1/cgroup");
        std::string line;
        while (std::getline(cgroupFile, line)) {
            if (line.find("/docker") != std::string::npos ||
                line.find("/lxc") != std::string::npos ||
                line.find("/kubepods") != std::string::npos ||
                line.find("/system.slice") != std::string::npos ||
                line == "0::/") {
                return true;
            }
        }
        return std::ifstream("/.dockerenv").good() ||
               std::ifstream("/run/.containerenv").good();
    }

    // 获取容器稳定ID
    static std::string getContainerStableId() {
        std::string id = getIdFromMountInfo();
        if (!id.empty()) return id;

        id = getIdFromCgroup();
        if (!id.empty()) return id;

        return getFallbackId();
    }

private:
    // 从mountinfo获取容器ID
    static std::string getIdFromMountInfo() {
        std::ifstream mountInfo("/proc/self/mountinfo");
        std::string line;
        while (std::getline(mountInfo, line)) {
            if (line.find("/docker/containers/") != std::string::npos) {
                size_t start = line.find("/docker/containers/") + 19;
                return line.substr(start, 64);
            }
        }
        return "";
    }

    // 从cgroup获取容器ID
    static std::string getIdFromCgroup() {
        std::ifstream cgroupFile("/proc/self/cgroup");
        std::string line;
        while (std::getline(cgroupFile, line)) {
            if (line.find("docker") != std::string::npos) {
                size_t pos = line.find_last_of("/");
                if (pos != std::string::npos) {
                    return line.substr(pos + 1);
                }
            }
        }
        return "";
    }

    // 获取备选ID
    static std::string getFallbackId() {
        // 尝试获取网络命名空间ID
        std::ifstream nsFile("/proc/self/ns/net");
        if (nsFile.is_open()) {
            std::string id;
            std::getline(nsFile, id);
            if (!id.empty()) return id;
        }

        // 尝试获取MAC地址
        std::ifstream netFile("/sys/class/net/eth0/address");
        if (netFile.is_open()) {
            std::string macAddr;
            std::getline(netFile, macAddr);
            if (!macAddr.empty()) return macAddr;
        }

        // 尝试获取主机名
        char hostname[256];
        if (gethostname(hostname, sizeof(hostname)) == 0) {
            return std::string(hostname);
        }

        return "";
    }
};

static FUNCTION_RETURN generate_docker_pc_id(std::array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> &data) {

	auto id = DockerDetector::getContainerStableId();
	if (id == "") {
		return FUNC_RET_NOT_AVAIL;
	}

	std::array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> identifier = {};
	strncpy((char *)&identifier, id.c_str(), identifier.size() - 1);
	data = identifier;
	return FUNC_RET_OK;
}

DockerStrategy::DockerStrategy() {}

DockerStrategy::~DockerStrategy() {}

LCC_API_HW_IDENTIFICATION_STRATEGY DockerStrategy::identification_strategy() const {
	return STRATEGY_CONTAINER_ID;
}

std::vector<HwIdentifier> DockerStrategy::alternative_ids() const {
	std::array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> data;
	FUNCTION_RETURN result = generate_docker_pc_id(data);
	std::vector<HwIdentifier> identifiers;
	if (result == FUNC_RET_OK) {
		identifiers.reserve(1);
		HwIdentifier pc_id;
		pc_id.set_identification_strategy(identification_strategy());
		pc_id.set_data(data);
		identifiers.push_back(pc_id);
	}
	return identifiers;
}

}  // namespace hw_identifier
} /* namespace license */
